package org.sws.infrastructure.persistent.repository;

import cn.bugstack.middleware.db.router.strategy.IDBRouterStrategy;
import com.alibaba.fastjson2.JSON;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.support.TransactionTemplate;
import org.sws.domain.award.model.valobj.AccountStatusVO;
import org.sws.domain.credit.aggregate.TradeAggregate;
import org.sws.domain.credit.model.entity.CreditAccountEntity;
import org.sws.domain.credit.model.entity.CreditOrderEntity;
import org.sws.domain.credit.model.entity.CreditTaskEntity;
import org.sws.domain.credit.repository.ICreditRepository;
import org.sws.infrastructure.event.EventPublisher;
import org.sws.infrastructure.persistent.dao.ITaskDao;
import org.sws.infrastructure.persistent.dao.IUserCreditAccountDao;
import org.sws.infrastructure.persistent.dao.IUserCreditOrderDao;
import org.sws.infrastructure.persistent.po.Task;
import org.sws.infrastructure.persistent.po.UserCreditAccount;
import org.sws.infrastructure.persistent.po.UserCreditOrder;
import org.sws.infrastructure.redis.IRedisService;
import org.sws.types.common.Constants;
import org.sws.types.enums.ResponseCode;
import org.sws.types.exception.AppException;

import java.math.BigDecimal;
import java.util.concurrent.TimeUnit;

/**
 * @Author sws
 * @Date 2025/4/1 15:49
 * @description:
 */
@Slf4j
@Repository
@RequiredArgsConstructor
public class CreditRepository implements ICreditRepository {

    private final IRedisService redisService;
    private final IUserCreditAccountDao userCreditAccountDao;
    private final IUserCreditOrderDao userCreditOrderDao;
    private final IDBRouterStrategy dbRouter;
    private final TransactionTemplate transactionTemplate;
    private final ITaskDao taskDao;
    private final EventPublisher eventPublisher;
    @Override
    public void saveUserCreditTradeOrder(TradeAggregate tradeAggregate) {
        String userId = tradeAggregate.getUserId();
        CreditAccountEntity creditAccountEntity = tradeAggregate.getCreditAccountEntity();
        CreditOrderEntity creditOrderEntity = tradeAggregate.getCreditOrderEntity();

        // 积分账户
        UserCreditAccount userCreditAccountReq = new UserCreditAccount();
        userCreditAccountReq.setUserId(userId);
        userCreditAccountReq.setTotalAmount(creditAccountEntity.getAdjustAmount());
        userCreditAccountReq.setAvailableAmount(creditAccountEntity.getAdjustAmount());
        userCreditAccountReq.setAccountStatus(AccountStatusVO.open.getCode());

        // 积分订单
        UserCreditOrder userCreditOrderReq = new UserCreditOrder();
        userCreditOrderReq.setUserId(creditOrderEntity.getUserId());
        userCreditOrderReq.setOrderId(creditOrderEntity.getCreditOrderId());
        userCreditOrderReq.setTradeName(creditOrderEntity.getTradeName().getName());
        userCreditOrderReq.setTradeType(creditOrderEntity.getTradeType().getCode());
        userCreditOrderReq.setTradeAmount(creditOrderEntity.getTradeAmount());
        userCreditOrderReq.setOutBusinessNo(creditOrderEntity.getOutBusinessNo());

        // 任务对象
        CreditTaskEntity creditTaskEntity = tradeAggregate.getCreditTaskEntity();
        Task task = new Task();
        if(null != creditTaskEntity){
            task.setUserId(creditTaskEntity.getUserId());
            task.setTopic(creditTaskEntity.getTopic());
            task.setState(creditTaskEntity.getState().getCode());
            task.setMessageId(creditTaskEntity.getMessageId());
            task.setMessage(JSON.toJSONString(creditTaskEntity.getMessage()));
        }

        RLock lock = redisService.getLock(Constants.RedisKey.USER_CREDIT_ACCOUNT_LOCK + userId + Constants.UNDERLINE + creditOrderEntity.getOutBusinessNo());
        try {
            lock.lock(3, TimeUnit.SECONDS);
            dbRouter.doRouter(userId);
            transactionTemplate.executeWithoutResult(status -> {
                try {
                    // 1. 新增/更新用户积分账户
                    UserCreditAccount userCreditAccount = userCreditAccountDao.queryUserCreditAccount(userCreditAccountReq);
                    if (null == userCreditAccount) {
                        userCreditAccountDao.insert(userCreditAccountReq);
                    } else {
                        BigDecimal availableAmount = userCreditAccountReq.getAvailableAmount();
                        if (availableAmount.compareTo(BigDecimal.ZERO) >= 0) {
                            userCreditAccountDao.updateAddAmount(userCreditAccountReq);
                        } else {
                            int subtractionCount = userCreditAccountDao.updateSubtractionAmount(userCreditAccountReq);
                            if (1 != subtractionCount) {
                                status.setRollbackOnly();
                                throw new AppException(ResponseCode.CREDIT_ACCOUNT_SURPLUS_ERROR.getCode(), ResponseCode.CREDIT_ACCOUNT_SURPLUS_ERROR.getInfo());
                            }
                        }
                    }
                    // 2. 保存账户订单
                    userCreditOrderDao.insert(userCreditOrderReq);
                    if(null != task){
                        // 3。 写入任务记录
                        taskDao.insert(task);
                    }
                } catch (DuplicateKeyException e) {
                    status.setRollbackOnly();
                    log.error("调整账户积分额度异常，唯一索引冲突 userId:{} orderId:{}", userId, creditOrderEntity.getCreditOrderId(), e);
                }catch (Exception e) {
                    status.setRollbackOnly();
                    log.error("调整账户积分额度失败 userId:{} orderId:{}", userId, creditOrderEntity.getCreditOrderId(), e);
                }
            });
        } finally {
            dbRouter.clear();
            if (lock.isLocked()) {
                lock.unlock();
            }
        }
        if (null != task){
            eventPublisher.syncPublish(task);
        }
    }

    @Override
    public CreditAccountEntity queryUserCreditAccount(String userId) {
        UserCreditAccount userCreditAccountReq = new UserCreditAccount();
        userCreditAccountReq.setUserId(userId);
        dbRouter.doRouter(userId);
        try {
            UserCreditAccount userCreditAccount = userCreditAccountDao.queryUserCreditAccount(userCreditAccountReq);
            BigDecimal availableAmount;
            if (null == userCreditAccount) {
                return null;
            }
            availableAmount = userCreditAccount.getAvailableAmount();
            return CreditAccountEntity.builder()
                    .userId(userCreditAccount.getUserId())
                    .totalAmount(userCreditAccount.getTotalAmount())
                    .availableAmount(availableAmount)
                    .accountStatus(AccountStatusVO.valueOf(userCreditAccount.getAccountStatus()))
                    .build();
        } finally {
            dbRouter.clear();
        }
    }
}
